Skip to content

Loop daemon: bake resolved node/claude PATH into pr-loop.service unit - #117

Merged
robercano-ghbot merged 2 commits into
mainfrom
feat/issue-107-harden-runtime-env
Jul 15, 2026
Merged

Loop daemon: bake resolved node/claude PATH into pr-loop.service unit#117
robercano-ghbot merged 2 commits into
mainfrom
feat/issue-107-harden-runtime-env

Conversation

@robercano-ghbot

Copy link
Copy Markdown
Collaborator

Closes #107

What

The installed pr-loop.service (the loop daemon itself) got no baked PATH and ran under systemd's minimal PATH — gh but neither node nor claude — silently stalling node-dependent tick steps (loop-census.sh, merge-ready.sh, write_tick_record) until the daemon's runtime nvm fallback kicked in.

  1. arm-loop.sh now resolves node's dir (erroring loudly if unresolvable, matching the existing claude check) and bakes a deduped PATH (node dir, claude dir, standard system dirs) into the unit via a new __PATH__ placeholder in both pr-loop.service template copies.
  2. The nvm-sourcing fallback in loop-daemon.sh stays as a safety net for installs that predate this change or use fnm/volta/system node.
  3. When startup env resolution fails, main() now appends an env-error ledger line (pure bash, no node needed), so a degraded start is visible in the run ledger.

Item 3 of the issue (empty cadence under the degraded env) traced to this same root cause — loop-census.sh needs node to emit its cadence= line — no separate bug found, no extra code change needed.

Tests

loop-daemon.test.sh gains regression scenario 6: strips node/claude from the daemon's PATH but exposes a fake nvm install via NVM_DIR, asserting the startup ensure_claude_on_path call resolves them onto PATH before run_once spawns the first tick's child. Suite green: PASS (21 checks), re-verified 2026-07-15.

Review

Correctness (opus) + tests (sonnet) lenses both approved on 2026-07-14; the driver was killed by a mid-flight service restart before it could push/open this PR (see #111). Branch recovered and published manually.

🤖 Generated with Claude Code

…107)

The installed pr-loop.service (the loop daemon itself) got no baked PATH
at all and ran under systemd's minimal PATH — gh but neither node nor
claude — silently stalling node-dependent tick steps (loop-census.sh,
merge-ready.sh, write_tick_record) until the daemon's own runtime
ensure_claude_on_path nvm fallback kicked in. arm-loop.sh now resolves
node's dir (erroring loudly if unresolvable, matching the existing claude
check) and bakes a deduped PATH (node dir, claude dir, standard system
dirs) into the unit via a new __PATH__ placeholder in both pr-loop.service
template copies. The nvm-sourcing fallback in loop-daemon.sh stays as a
safety net for installs that predate this change or use fnm/volta/system
node.

Also: when startup env resolution fails, main() now appends an
env-error ledger line (pure bash, no node needed) alongside the existing
warning log, so a degraded start is visible in the run ledger.

Adds a loop-daemon.test.sh regression scenario (6) that strips node/claude
from the daemon's PATH but exposes a fake nvm install via NVM_DIR,
asserting the startup ensure_claude_on_path call resolves them onto PATH
before run_once spawns the first tick's child process.

Item 3 (empty cadence under the degraded env) traced to the same root
cause as this fix — loop-census.sh needs node to emit its cadence= line —
no separate threading bug found, so no code change was needed there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… CI (#107)

CI failed where local passed: run_daemon_once strips claude off PATH but
left NVM_DIR uncontrolled, so on a dev box ensure_claude_on_path resolved
the HOST's ~/.nvm (no env-error ledger line) while CI runners — with no
nvm at all — hit main()'s new startup env-error path, creating the ledger
file that scenarios 1/2 asserted must not exist.

Pin NVM_DIR inside the fixture so the host's nvm can never leak in, and
assert the real invariant: the startup 'verdict=startup result=env-error'
line is the ONLY ledger line and no driver (verdict=advance) line is ever
written. This also gives the #107 env-error ledger feature its own
deterministic coverage (21 -> 23 checks).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@robercano-ghbot
robercano-ghbot merged commit 31cda4e into main Jul 15, 2026
9 checks passed
@robercano-ghbot
robercano-ghbot deleted the feat/issue-107-harden-runtime-env branch July 15, 2026 09:39
robercano added a commit that referenced this pull request Jul 15, 2026
… autostart tiers in setup (#119)

Field evidence from 2026-07-14/15 (5 of 6 drivers killed mid-flight by
daemon cgroup kills — service restarts, overnight VM downtime, wsl
shutdowns) showed three doc gaps:

- USAGE daemon section oversold setsid: a process group is not a cgroup,
  so any daemon stop kills in-flight drivers with no ledger line. Document
  the caveat, the check-before-restart procedure, and #119 as the fix.
- Failure contract: the in_flight wedge fix is now classify-then-recover
  (empty -> delete, publishable -> push + open PR as proven by #107 ->
  PR #117, dirty -> wip-commit and never delete), plus a new row for the
  daemon-killed-mid-driver fingerprint. New WSL2 caveat: autostart
  protects the queue, not a driver in flight.
- Setup skill step 4 now offers the verified unattended autostart tier
  (AtStartup + S4U + vmIdleTimeout=-1, per USAGE) above the logon-only
  schtasks variant, and states the mid-driver caveat either way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Loop daemon: harden runtime-env resolution — bake resolved PATH into installed units, don't depend on nvm sourcing at runtime

2 participants